home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / Book Chapters / 05 - User Interaction / Dungeon 2 / Dungeon2.c < prev    next >
Text File  |  1995-04-01  |  21KB  |  785 lines

  1.  
  2. /*Dungeon2 – prototype game example for the Mac Game book*/
  3. /*By Ingemar Ragnemalm 1995*/
  4. /**/
  5. /*This game is somewhat similar to MemoryGame, in that it uses a grid, represented*/
  6. /*by an array. The game is a typical (though extremely simplified) dungeon-digging game,*/
  7. /*where the objective is to collect treasures and fight monsters.*/
  8.  
  9. /*Representation:*/
  10. /*The array tileArr holds nearly all information we need. telling what is in each space in the grid.*/
  11. /*Monsters and treasures are only represented this way. In a real game, you may wish to*/
  12. /*keep a list of all monster and treasure positions, to avoid scanning for them and to keep more*/
  13. /*information about each.*/
  14. /*The game also keeps an array that tells what spaces are known to the player (tilesKnown), and*/
  15. /*the player position (playerPosition), so we don't have to scan for it all the time.*/
  16. /*The combat system is extremely simple. If you try moving to an enemy, you have 60% chance to*/
  17. /*hit it and kill it. If an enemy tries to move to you, it has 50% chance to hit, reducing your hit points*/
  18. /*by one.*/
  19. /**/
  20. /*The program ends when we select "Quit".*/
  21.  
  22. /*This version adds the following features:*/
  23. /*- Keyboard control as well as mouse control*/
  24. /*- Event processing, menus etc*/
  25.  
  26.  
  27. #include <Sound.h>
  28.  
  29. /*Size of the array*/
  30. #define    kArraySizeH 15
  31. #define    kArraySizeV 12
  32.  
  33. /*Size of the tiles*/
  34. #define    kTileSizeH 32
  35. #define    kTileSizeV 32
  36.  
  37. /*Menu ids*/
  38. #define    appleID 127
  39. #define    fileID 128
  40.  
  41. /* A macro for taking the abs of a value */
  42. #define abs(x) (x>0?x:-x)
  43.  
  44. /* All the possible states of a tile (space in the dungeon) */
  45. typedef enum {empty, wall, player, enemy, tempEnemy, gold, exitPos} TileState;
  46.     
  47. /* The window pointer */
  48. WindowPtr myWindow;
  49.  
  50. /* Arrays describing the dungeon */
  51.  
  52. /* What tiles have we seen? */
  53. Boolean tileKnown[kArraySizeH][kArraySizeV];
  54. /* What does each tile contain? */
  55. TileState tileArray[kArraySizeH][kArraySizeV];
  56.  
  57. /*Variables describing the player:*/
  58. Point playerPosition;
  59. short playerHitPoints;
  60.  
  61. /* A boolean telling if we should quit yet or not */
  62. Boolean gDone = false;
  63.  
  64. /*Pictures*/
  65. PicHandle floorTile;
  66. PicHandle playerTile;
  67. PicHandle enemyTile;
  68. PicHandle goldTile;
  69. PicHandle wallTile;
  70. PicHandle exitTile;
  71.  
  72. /*All 8 directions as vectors*/
  73. Point directionTable[8] = { { 0, 1 },
  74.                           {-1, 1 },
  75.                           {-1, 0 },
  76.                           {-1,-1 },
  77.                           { 0,-1 },
  78.                           { 1,-1 },
  79.                           { 1, 0 },
  80.                           { 1, 1 }};
  81.  
  82. /* A function that generates a value in the interval 0..range-1 */
  83.  
  84. static short Rand(short range)
  85. {
  86.     return (Random () & 0x7fff) % range;
  87. }; /*Rand*/
  88.  
  89.  
  90. /* Draw a tile */
  91.  
  92. static void DrawTile(short h, short v)
  93. {
  94.     Rect tileRectangle;
  95.  
  96.     SetRect(&tileRectangle, h * kTileSizeH, v * kTileSizeV, (h + 1) * kTileSizeH, (v + 1) * kTileSizeV);
  97.     if ( tileKnown[h][v] )
  98.         switch ( tileArray[h][v] )
  99.         {
  100.             case empty: 
  101.                 DrawPicture(floorTile, &tileRectangle); break;
  102.             case wall: 
  103.                 DrawPicture(wallTile, &tileRectangle); break;
  104.             case player: 
  105.                 DrawPicture(playerTile, &tileRectangle); break;
  106.             case enemy:
  107.             case tempEnemy: 
  108.                 DrawPicture(enemyTile, &tileRectangle); break;
  109.             case gold: 
  110.                 DrawPicture(goldTile, &tileRectangle); break;
  111.             case exitPos: 
  112.                 DrawPicture(exitTile, &tileRectangle); break;
  113.             default:
  114.                 PaintRect(&tileRectangle);
  115.         }
  116.     else
  117.         PaintRect(&tileRectangle);
  118. } /*DrawTile*/
  119.  
  120.  
  121. /* Set all tiles around the player to be known, and draw them if they were not known before. */
  122.  
  123. static void ShowAroundPlayer()
  124. {
  125.     short h, v;
  126.  
  127.     for ( h = playerPosition.h - 1 ; h <= playerPosition.h + 1 ; h++)
  128.         for ( v = playerPosition.v - 1 ; v <= playerPosition.v + 1 ; v++)
  129.             if ( ! tileKnown[h][v] )
  130.                 {
  131.                     tileKnown[h][v] = true;
  132.                     DrawTile(h, v);
  133.                 }
  134. } /*ShowAroundPlayer*/
  135.  
  136.  
  137. /* The level generation routine */
  138.  
  139. static void CreateLevel()
  140. {
  141.     Rect roomRect[10];
  142.     short room;
  143.     short height, width;
  144.     short h, v, h1, v1, h2, v2;
  145.     short numRooms;
  146.     short numTreasures, numMonsters;
  147.     short i;
  148.  
  149. /*For each tile position, we set the initial state.*/
  150. /**/
  151. /*Here we generate the dungeon randomly, with a rather simple algorithm.*/
  152. /*Most real games use pre-designed levels, preferrably stored in resources.*/
  153.  
  154.  
  155. /*Dungeon generation algorithm:*/
  156. /*We select a number of rooms to be placed, 3-10*/
  157. /*Each room is randomly assigned a size, and then a position.*/
  158. /*In each room we may put monsters and/or treasures.*/
  159. /*From each room except the last, we make a path from the room to the next.*/
  160. /*This guarantees that all rooms are connected.*/
  161.  
  162.  
  163. /*First fill the entire dungeon with walls!*/
  164.         for ( h = 0 ; h < kArraySizeH ; h++)
  165.             for ( v = 0 ; v < kArraySizeV ; v++)
  166.                     tileArray[h][v] = wall;
  167.  
  168.         numRooms = 3 + Rand(4); /*3 to 6 rooms*/
  169.  
  170. /*Create each room*/
  171.         for ( room = 0 ; room <= numRooms - 1 ; room++)
  172.             {
  173.                 height = 1 + Rand(5 - numRooms / 2);
  174.                 width = 1 + Rand(5 - numRooms / 2);
  175.                 roomRect[room].top = 1 + Rand(kArraySizeV - height - 2);
  176.                 roomRect[room].bottom = roomRect[room].top + height;
  177.                 roomRect[room].left = 1 + Rand(kArraySizeH - width - 2);
  178.                 roomRect[room].right = roomRect[room].left + width;
  179.  
  180.                 for ( h = roomRect[room].left ; h <= roomRect[room].right ; h++)
  181.                     for ( v = roomRect[room].top ; v <= roomRect[room].bottom ; v++)
  182.                             tileArray[h][v] = empty;
  183.             };
  184.  
  185. /*Make paths between all rooms*/
  186.         for ( room = 0 ; room <= numRooms - 2 ; room++)
  187.             {
  188. /*We make a path from h1, h2, to h2, v2*/
  189.                 h1 = roomRect[room].left + Rand(roomRect[room].right - roomRect[room].left + 1);
  190.                 v1 = roomRect[room].top + Rand(roomRect[room].bottom - roomRect[room].top + 1);
  191.                 h2 = roomRect[room + 1].left + Rand(roomRect[room + 1].right - roomRect[room + 1].left + 1);
  192.                 v2 = roomRect[room + 1].top + Rand(roomRect[room + 1].bottom - roomRect[room + 1].top + 1);
  193.  
  194. /*First move along the h axis*/
  195.                 if ( h1 < h2 )
  196.                     for ( h = h1 ; h <= h2 ; h++)
  197.                             tileArray[h][v1] = empty;
  198.                 else
  199.                     for ( h = h1 ; h >= h2 ; h--)
  200.                             tileArray[h][v1] = empty;
  201. /*And then along the v axis*/
  202.                 if ( v1 < v2 )
  203.                     for ( v = v1 ; v <= v2 ; v++)
  204.                             tileArray[h2][v] = empty;
  205.                 else
  206.                     for ( v = v1 ; v >= v2 ; v--)
  207.                             tileArray[h2][v] = empty;
  208.             };
  209.  
  210. /*Now populate the rooms!*/
  211.         for ( room = 1 ; room <= numRooms - 1 ; room++)
  212.             {
  213.                 numTreasures = Rand(3);        /*0 to 2 treasures*/
  214.                 for ( i = 1 ; i <= numTreasures ; i++)
  215.                     {
  216.                         h = roomRect[room].left + Rand(roomRect[room].right - roomRect[room].left + 1);
  217.                         v = roomRect[room].top + Rand(roomRect[room].bottom - roomRect[room].top + 1);
  218.                         tileArray[h][v] = gold;
  219.                     };
  220.                 numMonsters = Rand(2);        /*0 to 1 monsters*/
  221.                 for ( i = 1 ; i <= numMonsters; i++)
  222.                     {
  223.                         h = roomRect[room].left + Rand(roomRect[room].right - roomRect[room].left + 1);
  224.                         v = roomRect[room].top + Rand(roomRect[room].bottom - roomRect[room].top + 1);
  225.                         tileArray[h][v] = enemy;
  226.                     };
  227.             };
  228.  
  229. /*Finally, place the player in the first room and the exit in the last. Also check that the exit is*/
  230. /*not the same position as the player! (It can happen, since we don't mak sure that rooms don't*/
  231. /*overlap!)*/
  232.  
  233. /*Player position:*/
  234.         playerPosition.h = roomRect[0].left + Rand(roomRect[0].right - roomRect[0].left + 1);
  235.         playerPosition.v = roomRect[0].top + Rand(roomRect[0].bottom - roomRect[0].top + 1);
  236.         tileArray[playerPosition.h][playerPosition.v] = player;
  237. /*Exit position:*/
  238.         h = roomRect[numRooms - 1].left + Rand(roomRect[numRooms - 1].right - roomRect[numRooms - 1].left + 1);
  239.         v = roomRect[numRooms - 1].top + Rand(roomRect[numRooms - 1].bottom - roomRect[numRooms - 1].top + 1);
  240. /*But please don't overwrite the player with the exit!*/
  241.         if ( h == playerPosition.h )
  242.             if ( v == playerPosition.v )
  243.                 {
  244. /*Try another room:*/
  245.                     h = roomRect[numRooms - 2].left + Rand(roomRect[numRooms - 2].right - roomRect[numRooms - 2].left + 1);
  246.                     v = roomRect[numRooms - 2].top + Rand(roomRect[numRooms - 2].bottom - roomRect[numRooms - 2].top + 1);
  247.  
  248. /*Still failure? Darn. Just take a space next to the player.*/
  249.                     if ( h == playerPosition.h )
  250.                         if ( v == playerPosition.v )
  251.                             if ( h < kArraySizeH )
  252.                                 h = h + 1;
  253.                             else
  254.                                 h = h - 1;
  255.                 };
  256. /*OK, that's enough. Set the exit!*/
  257.         tileArray[h][v] = exitPos;
  258.  
  259. /*All tiles are unknown when we start*/
  260.         for ( h = 0 ; h < kArraySizeH ; h++)
  261.             for ( v = 0 ; v < kArraySizeV ; v++)
  262.                 tileKnown[h][v] = false;
  263. /*…except the ones around the player*/
  264.  
  265. /*Make the spaces around the player known*/
  266.         ShowAroundPlayer();
  267.  
  268. /*Draw all tiles!*/
  269.         for ( h = 0 ; h < kArraySizeH ; h++)
  270.             for ( v = 0 ; v < kArraySizeV ; v++)
  271.                 DrawTile(h, v);
  272. } /*CreateLevel*/
  273.  
  274.  
  275. /* Move an enemy */
  276.  
  277. static void MoveEnemy(short h, short v)
  278. {
  279.         short dist;
  280.         short newh, newv;
  281.         short dir;
  282.  
  283. /*1: decide if we are close to enough to the player to "hear" the player*/
  284.  
  285.     dist = abs(h - playerPosition.h) + abs(v - playerPosition.v); /*City Block distance*/
  286.  
  287. /*2: Make a suggested destination, newh, newv*/
  288.  
  289.     if ( dist < Rand(15) )                    /*Move towards the player*/
  290.         {
  291.             if ( h < playerPosition.h )
  292.                 newh = h + 1;
  293.             else if ( h > playerPosition.h )
  294.                 newh = h - 1;
  295.             else
  296.                 newh = h;
  297.             if ( v < playerPosition.v )
  298.                 newv = v + 1;
  299.             else if ( v > playerPosition.v )
  300.                 newv = v - 1;
  301.             else
  302.                 newv = v;
  303.         }
  304.     else                                    /*Move randomly*/
  305.         { 
  306.             dir = Rand(8);
  307.             newh = h + directionTable[dir].h;
  308.             newv = v + directionTable[dir].v;
  309.         };
  310.  
  311. /*3: Check what is in the destination and take appropriate action (move, fight)*/
  312.  
  313.     switch ( tileArray[newh][newv] )
  314.     {
  315.         case empty: 
  316.                 tileArray[newh][newv] = tempEnemy;    /*We can't use "enemy", since then we might process it again in the same move!*/
  317.                 tileArray[h][v] = empty;
  318.                 DrawTile(newh, newv);
  319.                 DrawTile(h, v);
  320.                 break;
  321.         case player: 
  322.             if ( Rand(10) > 5 )                            /*Does it hit?*/
  323.             {                                        /*Enemy hits player!*/
  324.                 playerHitPoints--;                    /*Reduce player hit points*/
  325.                 if ( playerHitPoints > 0 )
  326.                 {                                        /*Player still lives!*/
  327.                     if ( noErr != SndPlay(nil, (SndListHandle)GetNamedResource('snd ', "\pPlayer hit"), false) )
  328.                         ;
  329.                 }
  330.                 else
  331.                 {                                /*Player died!*/
  332.                     if ( noErr != SndPlay(nil, (SndListHandle)GetNamedResource('snd ', "\pPlayer died"), false) )
  333.                         ;
  334.                 };
  335.             }
  336.             else
  337.             {                                    /*Miss!*/
  338.                 if ( noErr != SndPlay(nil, (SndListHandle)GetNamedResource('snd ', "\pPlayer miss"), false) )
  339.                     ;
  340.             };
  341.             break;
  342.          case wall:
  343.         case enemy:
  344.         case gold:
  345.         case exitPos:
  346.         case tempEnemy: 
  347.             ;                                            /*Don't move to any of these!*/
  348.     }; /*case*/
  349. } /*MoveEnemy*/
  350.  
  351.  
  352. /* Initialize - create window, load graphics */
  353.  
  354. static void InitDungeon()
  355. {
  356.     Rect windowRectangle;
  357.  
  358. /*Set up the window*/
  359.     SetRect(&windowRectangle, 50, 50, 50 + kArraySizeH * kTileSizeH, 50 + kArraySizeV * kTileSizeV);
  360.     myWindow = NewCWindow(nil, &windowRectangle, "\pDungeon 2", true, documentProc, (WindowPtr)-1L, false, 0);
  361.     SetPort(myWindow);
  362.  
  363.     qd.randSeed = TickCount ();    /*Seed the random number generator*/
  364.  
  365. /*Load all pictures*/
  366.     floorTile = GetPicture(128);            /*PICT resource #128.*/
  367.     playerTile = GetPicture(129);            /*PICT resource #129.*/
  368.     enemyTile = GetPicture(130);            /*PICT resource #130.*/
  369.     goldTile = GetPicture(131);                /*PICT resource #131.*/
  370.     wallTile = GetPicture(132);                /*PICT resource #132.*/
  371.     exitTile = GetPicture(133);                /*PICT resource #133.*/
  372. } /*InitDungeon*/
  373.  
  374.  
  375. /* Set up for a new game */
  376.  
  377. static void NewGame()
  378. {
  379. /* Fill in the tileArr array */
  380.         CreateLevel();
  381.  
  382. /* Start with a healthy player */
  383.         playerHitPoints = 5;
  384. } /*NewGame*/
  385.  
  386.  
  387. /* ValidMove checks if a tile clickedTile is inside the array bounds *and* near the player */
  388.  
  389. static Boolean ValidMove(Point clickedTile)
  390. {
  391. /* Valid tile?*/
  392.     if ( clickedTile.h >= 0 )
  393.         if ( clickedTile.v >= 0 )
  394.             if ( clickedTile.h < kArraySizeH )
  395.                 if ( clickedTile.v < kArraySizeV )
  396. /* OK, we are inside the game area, clicking in some space! Is it next to the player?*/
  397.                     if ( clickedTile.h >= playerPosition.h - 1 )
  398.                         if ( clickedTile.h <= playerPosition.h + 1 )
  399.                             if ( clickedTile.v >= playerPosition.v - 1 )
  400.                                 if ( clickedTile.v <= playerPosition.v + 1 ) 
  401.                                     return true;
  402.     return false;
  403. } /*ValidMove*/
  404.  
  405.  
  406. /* Try to move the player to the position where we clicked. */
  407.  
  408. static void MovePlayer(Point clickedTile)
  409. {
  410. short h, v;
  411.  
  412. /* Valid move?*/
  413.     if (ValidMove(clickedTile))
  414. /* Yes! What is there? */
  415.     { 
  416.         switch ( tileArray[clickedTile.h][clickedTile.v] )
  417.             {
  418.             case empty:
  419.                     tileArray[playerPosition.h][playerPosition.v] = empty;
  420.                     tileArray[clickedTile.h][clickedTile.v] = player;
  421.                     DrawTile(playerPosition.h, playerPosition.v);
  422.                     DrawTile(clickedTile.h, clickedTile.v);
  423.                     playerPosition = clickedTile;
  424.                     break;
  425.             case gold: 
  426.                     tileArray[playerPosition.h][playerPosition.v] = empty;
  427.                     tileArray[clickedTile.h][clickedTile.v] = player;
  428.                     DrawTile(playerPosition.h, playerPosition.v);
  429.                     DrawTile(clickedTile.h, clickedTile.v);
  430.                     playerPosition = clickedTile;
  431. /* We could add score here */
  432.                     if ( noErr != SndPlay(nil, (SndListHandle)GetNamedResource('snd ', "\pMoney"), false) )
  433.                         ;
  434.                     break;
  435.             case wall: 
  436.                 SysBeep(1);
  437.                 break;
  438.             case enemy: 
  439.                 if ( Rand(10) > 4 )
  440.                     { /*Hit! Play a "monster died" sound*/
  441.                         if ( noErr != SndPlay(nil, (SndListHandle)GetNamedResource('snd ', "\pEnemy died"), false) )
  442.                             ;
  443.                         /* Walk to the space where the monster was.*/
  444.                         tileArray[playerPosition.h][playerPosition.v] = empty;
  445.                         tileArray[clickedTile.h][clickedTile.v] = player;
  446.                         DrawTile(playerPosition.h, playerPosition.v);
  447.                         DrawTile(clickedTile.h, clickedTile.v);
  448.                         playerPosition = clickedTile;
  449.                     }
  450.                 else
  451.                     { /*Miss! Play the "miss" sound. */
  452.                         if ( noErr != SndPlay(nil, (SndListHandle)GetNamedResource('snd ', "\pEnemy miss"), false) )
  453.                             ;
  454.                     };
  455.                 break;
  456.                 /*attack!*/
  457.             case exitPos: 
  458.                 tileArray[playerPosition.h][playerPosition.v] = empty;
  459.                 tileArray[clickedTile.h][clickedTile.v] = player;
  460.                 DrawTile(playerPosition.h, playerPosition.v);
  461.                 DrawTile(clickedTile.h, clickedTile.v);
  462.                 playerPosition = clickedTile;
  463.                 if ( noErr != SndPlay(nil, (SndListHandle)GetNamedResource('snd ', "\pNext level"), false) )
  464.                     ;
  465.                 CreateLevel();    /* Don't quit - make a new level instead */
  466.                 break;
  467.         }; /*case*/
  468.     };
  469.  
  470. ShowAroundPlayer();
  471.  
  472. /* Monsters are allowed to move now!*/
  473.  
  474. /* Move all enemies!*/
  475. for ( h = 0 ; h < kArraySizeH ; h++)
  476.     for ( v = 0 ; v < kArraySizeV ; v++)
  477.         if ( tileArray[h][v] == enemy )
  478.             MoveEnemy(h, v);
  479. /* After moving, replace tempEnemy by enemy.*/
  480. for ( h = 0 ; h < kArraySizeH ; h++)
  481.     for ( v = 0 ; v < kArraySizeV ; v++)
  482.         if ( tileArray[h][v] == tempEnemy )
  483.             tileArray[h][v] = enemy;
  484. } /*MovePlayer*/
  485.  
  486.  
  487. /* Handle mouse downs */
  488.  
  489. static void DoMouse(Point clickPoint, long mods)
  490. {
  491.     Point clickedTile;
  492.  
  493. /* If the hero is dead, we can't move! */
  494.     if ( playerHitPoints < 1 )
  495.     {
  496.         SysBeep(1);
  497.         return;
  498.     };
  499.  
  500. /*Convert clickPoint to local coordinates*/
  501.     GlobalToLocal(&clickPoint);
  502. /*Convert it to coordinates in the arrays.*/
  503.     clickedTile.h = clickPoint.h / kTileSizeH;
  504.     clickedTile.v = clickPoint.v / kTileSizeV;
  505.     MovePlayer(clickedTile);
  506. } /*DoMouse*/
  507.  
  508.  
  509. /* Empty stub for a background task. */
  510.  
  511. static void DoBackground()
  512. {
  513. } /*DoBackground*/
  514.  
  515.  
  516. /* Add two points - simplifies the keydown handler */
  517.  
  518. static Point AddPoints(Point p1,Point p2)
  519. {
  520.     Point dest;
  521.  
  522.     dest.h = p1.h + p2.h;
  523.     dest.v = p1.v + p2.v;
  524.     return dest;
  525. }; /*AddPoints*/
  526.  
  527.  
  528. /* Handle key downs */
  529.  
  530. static void DoKey(char theKey, long mods)
  531. {
  532. /* If the hero is dead, we can't move! */
  533.     if ( playerHitPoints < 1 )
  534.         {
  535.             SysBeep(1);
  536.             return;
  537.         };
  538.  
  539. /* For now, we use a hard-coded key mapping. */
  540.     switch ( theKey )
  541.         {
  542.         case 'd':
  543.         case '6': 
  544.             MovePlayer(AddPoints(playerPosition, directionTable[0]));break;
  545.         case 'e':
  546.         case '9': 
  547.             MovePlayer(AddPoints(playerPosition, directionTable[1]));break;
  548.         case 'w':
  549.         case '8': 
  550.             MovePlayer(AddPoints(playerPosition, directionTable[2]));break;
  551.         case 'q':
  552.         case '7': 
  553.             MovePlayer(AddPoints(playerPosition, directionTable[3]));break;
  554.         case 'a':
  555.         case '4': 
  556.             MovePlayer(AddPoints(playerPosition, directionTable[4]));break;
  557.         case 'z':
  558.         case '1': 
  559.             MovePlayer(AddPoints(playerPosition, directionTable[5]));break;
  560.         case 'x':
  561.         case '2': 
  562.             MovePlayer(AddPoints(playerPosition, directionTable[6]));break;
  563.         case 'c':
  564.         case '3': 
  565.             MovePlayer(AddPoints(playerPosition, directionTable[7]));break;
  566.         default:
  567.             SysBeep(1);
  568.     }; /*case*/
  569. }; /*DoKey*/
  570.  
  571.  
  572. /* Handle selections in the File menu */
  573.  
  574. static void DoFileMenu(short item)
  575. {
  576.     switch ( item )
  577.         {
  578.         case 1: 
  579.             NewGame();break;
  580.         case 3: 
  581.             gDone = true;break;
  582.     }; /*case*/
  583. }; /*DoFileMenu*/
  584.  
  585.  
  586. /* The "About" item was selected */
  587.  
  588. static void DoAbout()
  589. {
  590.     short ignore;
  591.  
  592.     ignore = Alert(128, nil);
  593. }; /*DoAbout*/
  594.  
  595.  
  596. /* Handle update events */
  597.  
  598. static void DoUpdate()
  599. {
  600.         short h, v;
  601.  
  602.         BeginUpdate(myWindow);
  603. /*Draw all tiles!*/
  604.         for ( h = 0 ; h < kArraySizeH ; h++)
  605.             for ( v = 0 ; v < kArraySizeV ; v++)
  606.                 DrawTile(h, v);
  607.         EndUpdate(myWindow);
  608. }; /*DoUpdate*/
  609.  
  610.  
  611. /* Handle menu selections */
  612.  
  613. static void DoMenuSelection(long mSelect)
  614. {
  615.     short menuID;
  616.     short menuItem;
  617.     GrafPtr savePort;
  618.     Str255 name;
  619.     short ignore;
  620.  
  621.     menuID = HiWord(mSelect);
  622.     menuItem = LoWord(mSelect);
  623.  
  624.     switch ( menuID )
  625.         {
  626.         case appleID: 
  627.             if ( menuItem == 1 )
  628.                 DoAbout();
  629.             else
  630.                 {
  631.                     GetPort(&savePort);
  632.                     GetItem(GetMHandle(appleID), menuItem, name);
  633.                     ignore = OpenDeskAcc(name);
  634.                     SetPort(savePort);
  635.                 };
  636.             break;
  637.         case fileID: 
  638.             DoFileMenu(menuItem);break;
  639.         default:
  640.     ;}; /*case*/
  641.     HiliteMenu(0);
  642. }; /*DoMenuSelection*/
  643.  
  644.  
  645. /* Set up menus */
  646.  
  647. static void SetupMenus()
  648. {
  649.     MenuHandle appleMenu, fileMenu;
  650.     Str255 tempStr;
  651.  
  652.     tempStr[0]=1; tempStr[1] = (char)20;
  653.     appleMenu = NewMenu(appleID, tempStr); /*Apple menu symbol*/
  654.     InsertMenu(appleMenu, 0);
  655.     AppendMenu(appleMenu, "\pAbout Dungeon…;(-");
  656.     AppendResMenu(appleMenu, 'DRVR');
  657.  
  658.     fileMenu = GetMenu(128);
  659.     InsertMenu(fileMenu, 0);
  660.     DrawMenuBar ();
  661.  
  662. }; /*SetupMenus*/
  663.  
  664.  
  665. /* Main event loop */
  666.  
  667. static void MainLoop(void)
  668. {
  669. #define    kSleep 5 /*Real programs may modify the sleep time depending on whether or not they are in the front*/
  670.  
  671.     EventRecord    theEvent;
  672.     char    theKey;
  673.     long    whatSelection;
  674.     short    whichPart;
  675.     WindowPtr    whichWindow;
  676.     Point    diskInitPt = {40,40};
  677.  
  678. /*Get the next event with WaitNextEvent.*/
  679.     if ( WaitNextEvent(everyEvent, &theEvent, kSleep, nil) )
  680.         switch ( theEvent.what )
  681.         {
  682.             case mouseDown: 
  683. /*We must find out what kind of mouse down this was.*/
  684.                 whichPart = FindWindow(theEvent.where, &whichWindow);
  685.                 switch ( whichPart )
  686.                 {
  687.                     case inMenuBar:
  688. /*Click in menu bar. Let the system call MenuSelect track it.*/
  689.                         whatSelection = MenuSelect(theEvent.where);
  690.                         DoMenuSelection(whatSelection);    /*Our own routine for handling menu selections*/
  691.                         break;
  692.                     case inSysWindow:
  693. /*Click in some window that isn't ours*/
  694.                         SystemClick(&theEvent, whichWindow);
  695.                         break;
  696.                     case inGoAway:
  697. /*Click in close box of window. For "desk accessory"-style games, that is a good quit signal*/
  698.                         if ( (TrackGoAway(whichWindow, theEvent.where)) )
  699.                             gDone = true;
  700.                         break;
  701.                     case inDrag:
  702. /*Drag a window*/
  703.                         if ( (whichWindow != FrontWindow ()) && ((theEvent.modifiers & cmdKey) == 0) )
  704.                             SelectWindow(whichWindow);
  705.                         DragWindow(whichWindow, theEvent.where, &qd.screenBits.bounds);
  706.                         break;
  707.                     case inGrow: 
  708.                         ;  /*Ignored - we don't resize*/
  709.                         break;
  710.                     case inContent:
  711. /*Click in the window.*/
  712.                         if ( (whichWindow != FrontWindow ()) )
  713.                             SelectWindow(whichWindow);
  714.                         else
  715.                             DoMouse(theEvent.where, theEvent.modifiers); /*Go to application-specific mouse down handling*/
  716.                 }; /*case whichPart*/
  717.                 break; /*mouseDown*/
  718.             case keyDown:
  719.             case autoKey:
  720. /*If the command key is pressed, it is a menu selection*/
  721.                 theKey = (char)(theEvent.message & charCodeMask);
  722.                 if ( ((theEvent.modifiers & cmdKey) != 0) )
  723.                     DoMenuSelection(MenuKey(theKey));    /*Our own routine for handling menu selections*/
  724.                 else
  725. /*Otherwise, it's a normal key down.*/
  726.                     DoKey(theKey, theEvent.modifiers);
  727.                 break;
  728.             case updateEvt:
  729. /*Find out what event the update event is for.*/
  730.                 if ( (WindowPtr)theEvent.message == myWindow )
  731.                     DoUpdate();
  732.                 break;
  733.             case diskEvt:
  734. /*Handle bad disk insertions*/
  735.                 if (HiWord(theEvent.message) != noErr)
  736.                 {
  737.                     DILoad();
  738.                     DIBadMount(diskInitPt, theEvent.message);
  739.                     DIUnload();
  740.                 }
  741.                 break;
  742.             default: ; /*Other events are ignored*/
  743.         }; /*case*/
  744.  
  745. /*For each turn to the event loop, we may to call some "background" process, e.g. animations.*/
  746.     DoBackground();
  747. } /*MainLoop*/
  748.  
  749.  
  750. /* Standard inits */
  751.  
  752. static void InitToolbox(void) {
  753.     InitGraf (&qd.thePort);
  754.     InitFonts ();
  755.     FlushEvents (everyEvent,0);
  756.     InitWindows ();
  757.     InitMenus ();
  758.     TEInit ();
  759.     InitDialogs (nil);
  760.     InitCursor ();
  761. }
  762.  
  763.  
  764. /* Main program */
  765.  
  766. void main(void)
  767. {
  768.     InitToolbox();
  769.     InitDungeon();
  770.     SetupMenus();
  771.     NewGame();
  772. /*Initializations done! Run the game loop until the game ends.*/
  773.     do
  774.         {
  775.         MainLoop();
  776.     } while (!  gDone);
  777. } /*Dungeon*/
  778.  
  779.  
  780. /*What's left for making a real game of it?*/
  781. /*- Animations*/
  782. /*- Several levels*/
  783. /*- Faster drawing*/
  784. /*- Asynch sound*/
  785. /*- More objects, i.e. weapons, monsters, treasures…*/